8.2 TRUjQ - A Simple jQuery-like Library

  1. Learning outcomes
    • Learn and practice JavaScript more through the designing and implementation if a simple jQuery-like library.
    • Analyze, design, and implement a client-side libarry

  2. Observations on jQuery
    • Let's check again the typical coding style to use jQuery.
      $(document).ready(function() {  // ready(): event method
          $('p').click(function() {  // click(): event method
              $(this).css('background-color', 'Red');  // css(): action method
          });
      });
      $.get(url, function(data) { $('#message-pane').html(data); });  // get(): AJAX method; html(): action method
      
    • Do you have to use $ for your library? Can you use another name, such as TRUjQ, instead of $?
    • What kind of arguments is passed to $() in the above example?
      • document
      • string for CSS selectors
      • this as a HTMLElement object
      • a HTMLElement object
    • What is $ in the above example? Object? Function?
      • Function; Functions are objects.
    • What kind of value is returned from $ in the above example?
      • Not HTMLElement object
      • jQuery object that has event methods and action methods
    • What kind of property or method is defined in $ in the above example?
      • A method, .get()

  3. Requirements for a simple jQuery-like library, called TRUjQ
    • Usage: TRUjQ(selector).actionoreventmethod(); TRUjQ.ajaxmethod(); TRUjQ.property;
    • Selectors
      • document
      • string for CSS selectors - HTML element type, id, class, no other complex selectors (not difficult to implement though)
      • this as a DOM object, i.e., HTMLElement object
      • HTMLElement object
    • Return values from TRUjQ(selector)
      • Three different TRUjQ objects
        • TRUjQ_document - from TRUjQ(document)
        • TRUjQ_htmlselector - from TRUjQ('...')
        • TRUjQ_htmlelement - from TRUjQ(this) or TRUjQ(HTMLElement)
    • Event methods supported in TRUjQ objects
      • ready() for document (actually the 'load' event on window)
      • click()
      • dblclick()
    • Action methods supported in TRUjQ objects
      • html()
      • attr()
      • css()
      • hide(), show(), toggle()
    • AJAX methods supported in TRUjQ
      • get()
      • post()
    • Properties in TRUjQ
      • version - E.g., TRUjQ.version

  4. How to implement TRUjQ
    • TRUjQ()
      • How to make a function receive three different types of argument?
        • typeof argument == 'string'
        • argument instanceof HTMLElement
        • argument == document
        • How to combine the above three different types?
          function TRUjQ(x) {
              if (x == document) {
                  ...
                  return ...;
              } else if (typeof x == 'string') {
                  ...
                  return ...;
              } else if (x instanceof HTMLElement) { 
                  ...
                  return ...;
              } else  // unknown case
                  return null;
          }
          
      • How to make something work as a function and an object? E.g., TRUjQ.version.
        • Difficult? Here is an example.
          function TRUjQ(x) {
              if (x == document) { ... }
              else if (typeof x == 'string') { ... }
              else if (x instanceof HTMLElement) { ... }
              else { ... }
          }
          TRUjQ.version = '0.0.1';
          
      • Trial 1: Let's try the above example, with the following.



    • Warming up excercises
      • How to register an event listener?
      • How to select an HTML element with id?
      • How to select HTML elements with a class name?
        document.getElementsByClassName()
      • How to select HTML elements with a tag name?
        document.getElementsByTagName()
      • How to select HTML elements with a CSS selector?
        document.querySelectorAll()
      • Trial 1.5: Let's try the above selection cases, with the following. Note that this is the selected HTMLElement when event listeners are invoked.



      • Trial 1.6: Let's try another example of callback function and this. The this in TRUjQ_test_id('#...').click(function() { ...this... }) is not the element of the given id. Why?


      • How to make the this in TRUjQ_test_id('#...').click(function() { ...this... }) represent the element of the given id?

      • Trial 1.7: Let's try to preserve this for callback functions.



      • How to get CSS property values when they are not specified in the attribute 'style'?
        window.getComputedStyle(HTMLElement).property

    • TRUjQ() and three TRUjQ objects
      • Three TRUjQ objects:
        • TRUjQ_document - from TRUjQ(document)
        • TRUjQ_htmlselector - from TRUjQ('...')
        • TRUjQ_htmlelement - from TRUjQ(this)
      • Three events to support: load, click, dblclick
      • Similar to $(document).ready(...), $('...').click(...) and $(this).dblclick(...)

      • TRUjQ_document - How to make a TRUjQ object similar to the return value from jQuery $(document)?
        • Usage: TRUjQ(document).ready(function(){...});
        • What should TRUjQ(document) return?
          An object having a method, .ready(), that has an argument of a callback function that should be invoked when the document is completely loaded. This object also has some other methods to support events such as 'click' and 'dblclick'.
        • Here is an example.
          <script>
          //-- library -------------------------------
          
          function TRUjQ(x) {
              if ???? 
                  return ??? _TRUjQ_document(???);  // x; this should NOT be passed; this is window.
          }
          
          function _TRUjQ_document(thispreserved) {  // Object constructor
              this.ready = function(callback) {
                  if (document.readyState == 'complete') {
                      // callback();  // Should not be called directly. If so, the value of this in callback becomes different.
                      // Let's use the next interesting trick.
                      thispreserved.__TRUjQ_callback = callback;
                      ????
                      // or instead of the above trick, callback.bind(thispreserved)()
                      //   See https://www.w3schools.com/js/js_function_bind.asp
                      // or
                      // callback.call(thispreserved);  // https://www.w3schools.com/js/js_function_call.asp
                  } else {
                      ???.???(???, function() {;  // onload event on window; thispreserved should be used in the callback function.
                          ????
                      });
                  }
              }
              this.click = function(callback) {
                  document.addEventListener('click', function(eobj) {
                      //callback();  // It will not preserve 'this'. How to fix?
                      ????
                  });
              }
              // ????
          }
          </script>
          
          <script>
          //-- application ---------------------------
          
          TRUjQ(document).ready(function() {
              document.getElementById('tr2-div').innerHTML = 'Ready! Click me!<br>';
          });
          TRUjQ(document).click(function() {
              document.getElementById('tr2-div').innerHTML += (this == document ? 'this is document.' : 'this is not document.') + '<br>';
              document.getElementById('tr2-div').innerHTML += 'Clicked<br>';
          });
          </script>
          
          <div id='tr2-div'>Message here!</div>
          
        • Trial 2: Let's try the above example, with the following.



      • TRUjQ_htmlselector - How to make a TRUjQ object similar to the return value from $('...')?
        • Usage: TRUjQ('...').click(function(){...});, TRUjQ('...').dblclick(function(){...});
        • What should TRUjQ('...') return?
          An object having two methods,.click() and .dblclick(), that have an argument of a callback function that should be invoked when there is a 'click' or 'dblclick' event triggered on the target HTMLElement object. This object also has some action methods.
        • Here is an example. You may need to read VanillaJS for this example.
          <script>
          //-- library -------------------------------
          
          function TRUjQ(x) {
              if (x == document) {
                  return new _TRUjQ_document(x);
              }
              else if ???? 
                  return ??? _TRUjQ_htmlselector(x);
          }
          
          function _TRUjQ_document(thispreserved) {  // Object constructor
              this.ready = function(callback) {
                  if (document.readyState == 'complete') {
                      thispreserved.__TRUjQ_callback = callback;
                      thispreserved.__TRUjQ_callback();
                  } else 
                      window.addEventListener('load', function() {  // onload event on window
                          (callback.bind(thispreserved))();  // or, instead of the trick in the above
                      });
              }
              // ????
          }
          
          function _TRUjQ_htmlselector(selector) {
              selector = selector.trim();
              var elements = ????
              this.??? = function(callback) {
                  ????  // Registration of event listener over selected elements; how?
                        // Need to be careful with callback and this. What should be bound to callbak?
              }
              // ????
          }
          </script>
          
          <script>
          //-- application ---------------------------
          
          TRUjQ(document).ready(function() {
              TRUjQ('p').click(function() {
                  alert('Wow! Interesting! Tag selection, ' + this.innerHTML);
              });
              TRUjQ('#tr3-p').click(function() {
                  alert('Wow! Interesting! Id selection, ' + this.innerHTML);
              });
              TRUjQ('.tr3-p').click(function() {
                  alert('Wow! Interesting! Class selection, ' + this.innerHTML);
              });
          });
          </script>
          
          <p style='border:solid 1px Red'>You look great today.</p>
          <p id='tr3-p' style='border:solid 1px Blue'>You are a fantastic student.</p>
          <p class='tr3-p' style='border:solid 1px Green'>You have the best style.</p>
          
        • Trial 3: Let's try the above example, with the following.



      • TRUjQ_htmlelement - How to make a TRUjQ object similar to the return value from $(this)?
        • Usage: TRUjQ(this).css(); TRUjQ(this).hide(); ...
        • What should TRUjQ(this) return?
          An object having action methods. How?
        • Here is an example.
          <script>
          //-- library -------------------------------
          
          function TRUjQ(x) {
              if (typeof x == 'string')
                  return new _TRUjQ_htmlselector(x);
              else if (????) 
                  return new _TRUjQ_htmlelement(x);  // If HTMLElement object
          }
          
          function _TRUjQ_htmlselector(selector) {
              selector = selector.trim();
              var elements = document.querySelectorAll(selector);
              this.click = function(callback) {
                  for (var i = 0; i < elements.length; i++) {
                      elements[i].addEventListener('click', function(obj) { 
                          this.__TRUjQ_callback = callback;
                          this.__TRUjQ_callback(obj);
                      });
                  }
              }
              this.hide = ????
              // ????
          }
          
          function _TRUjQ_htmlelement(element) {  // element: HTMLElement object
              // ????
              this.css = function(property, value) {
                  if (value == ???)  // when value is not defined,
                      return ????;  // Using window.getComputedStyle()
                  else 
                      ????;  // Set property without using window.getComputedStyle()
              };
              // ????
          }
          </script>
          
          <script>
          //-- application ---------------------------
          
          TRUjQ('p').click(function() {
              TRUjQ(this).css('background-color', 'SkyBlue');
          });
          TRUjQ('#tr4-button').click(function() {
              TRUjQ('#tr4-p').hide();
          });
          </script>
          
          <p id='tr4-p' style='border:solid 1px Blue'>Click it to change the background color. You can do it!</p>
          <button id='tr4-button'>Hide it!</button>
          
        • Trial 4: Let's try the above example, with the following.



    • TRUjQ.get()
      • Usage: TRUjQ.get(url, callback);
      • Here is an example.
        <script>
        //-- library -------------------------------
        
        TRUjQ = ???;  // Let's start it with an empty object for testing.
        TRUjQ.??? = function(????) {
            let xhttp = ??? ???();  // AJAX object
            xhttp.addEventListener('load', function() {
                ????
            });
            xhttp.???("GET", url);
            xhttp.???();
        }
        </script>
        
        <script>
        //-- application ---------------------------
        
        //- Let's use jQuery for testing, except TRUjQ.get(). -----------------
        $('#tr5-send').click(function() {
            let url = `echo.php?message=????`;  // message from <input>
            TRUjQ.get(????);  // the message sent from the server needs to be displayed in <div>
        });
        </script>
        
        <input id='tr5-message'><br>
        <button id='tr5-send'>Send the above message to the echo server!</button>
        <div id='tr5-result'>Echoed message here!</div>
        
      • Trial 5: Let's try the above example, with the following.


      • Trial 6: Let's try to implement a different version of TRUjQ.get(). Here is an example how to use this version.
        const testTRUjQGet = async function(url) {
            let data = await TRUjQ.get(url);
            ...
        }
        testTRUjQGet("echo.php?message='I am happy to see your smile.'");
        
        Can you implement this version of TRUjq.get() so that the server-side can be accessed like a database?



    • TRUjQ.post() and TRUjQ(selector).load()
      • Usage: TRUjQ.post(url, queryobject, callback);, or ... = await TRUjQ.post(url, queryobject);
      • Usage: TRUjQ(selector).load(url);